package name.cyrax.red.jpv.impl.vectors;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumberAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAccessor;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.adapters.OmniNumberAdapter;
import name.cyrax.red.jpv.api.adapters.PrepareAdapter;
import name.cyrax.red.jpv.api.funcs.ArityFunctor;
import name.cyrax.red.jpv.api.funcs.BinaryFunctor;
import name.cyrax.red.jpv.api.funcs.CompiledInvocable;
import name.cyrax.red.jpv.api.funcs.Invocable;
import name.cyrax.red.jpv.api.funcs.InvocationContext;
import name.cyrax.red.jpv.api.funcs.UnaryFunctor;
import name.cyrax.red.jpv.api.vectors.Vector;
import name.cyrax.red.jpv.impl.adapters.NumericAdapters;
import name.cyrax.red.jpv.impl.funcs.Functions;

public abstract class VectorImpl<N extends Number> implements Vector<N>, 
	OmniNumberAdapter<N>, IntegerAdapter, LongAdapter, DoubleAdapter, NumberAdapter<N>, 
	CompiledInvocable<N>
{
	@Override public abstract int size();
	
	protected final boolean isValidIndex(int index)
	{
		return index >= 0 && index < size(); 
	}
	
	protected final void checkIndex(int index)
	{
		if (!isValidIndex(index))
			throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}

	private String outOfBoundsMsg(int index)
	{
        return "Index: " + index + ", Size: " + size();
	}

	@Override public OmniNumberAdapter<N> adapter()
	{
		return this;
	}
	
	@Override public IntegerAdapter asInteger()
	{
		return this;
	}

	@Override public LongAdapter asLong()
	{
		return this;
	}

	@Override public DoubleAdapter asDouble()
	{
		return this;
	}

	@Override public NumberAdapter<N> asNumber()
	{
		return this;
	}

	@Override public String toString()
	{
		return NumericAdapters.toString(new StringBuilder().append('V'), this).toString();
	}

	@Override public Vector<N> op(UnaryFunctor op)
	{
		return op.newInstance(this).invoke();
	}

	@Override public Vector<Integer> opInt(UnaryFunctor op)
	{
		return Functions.castInteger().newInstance(op.newInstance(this)).invoke();
	}

	@Override public Vector<Long> opLong(UnaryFunctor op)
	{
		return Functions.castLong().newInstance(op.newInstance(this)).invoke();
	}

	@Override public Vector<Double> opDouble(UnaryFunctor op)
	{
		return Functions.castDouble().newInstance(op.newInstance(this)).invoke();
	}

	@Override public Vector<N> op(BinaryFunctor op, Vector<?> v1)
	{
		return op.newInstance(this, v1).invoke();
	}

	@Override public Vector<Integer> opInt(BinaryFunctor op, Vector<?> v1)
	{
		return Functions.castInteger().newInstance(op.newInstance(this, v1)).invoke();
	}

	@Override public Vector<Long> opLong(BinaryFunctor op, Vector<?> v1)
	{
		return Functions.castLong().newInstance(op.newInstance(this, v1)).invoke();
	}

	@Override public Vector<Double> opDouble(BinaryFunctor op, Vector<?> v1)
	{
		return Functions.castDouble().newInstance(op.newInstance(this, v1)).invoke();
	}

	@Override public Vector<N> op(ArityFunctor op, Vector<?>[] vN)
	{
		return op.newInstance(this, vN).invoke();
	}

	@Override public Vector<Integer> opInt(ArityFunctor op, Vector<?>[] vN)
	{
		return op.newInteger(this, vN).invoke();
	}

	@Override public Vector<Long> opLong(ArityFunctor op, Vector<?>[] vN)
	{
		return op.newLong(this, vN).invoke();
	}

	@Override public Vector<Double> opDouble(ArityFunctor op, Vector<?>[] vN)
	{
		return op.newDouble(this, vN).invoke();
	}
	
	@Override public Vector<N> invoke()
	{
		return this;
	}

	@Override public CompiledInvocable<N> compile()
	{
		return this;
	}
	
	@Override public NumericAdapter getAdapter()
	{
		return this;
	}
	
	@Override public <T> T access(InvocationContext context, NumericAccessor<T> accessor)
	{
		return this.detectType(accessor);
	}
	
	@Override public boolean isCompilationContext(InvocationContext context)
	{
		return true; //Vector is native
	}
	
	@Override public PrepareAdapter prepare()
	{
		return null;
	}
	
	@Override public CompiledInvocable<N> compileAsSubstatement(InvocationContext context)
	{
		return this;
	}
	
	@Override public NumericAdapter getParam(int index)
	{
		return null;
	}
	
	@Override public Invocable<?> getInvocableParam(int index)
	{
		return null;
	}
}
